//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"

//SAGITTARIUS headers:
#include "sagittarius/parallel/pfor.h"
#include "sagittarius/parallel/atomics.h"
#include <malloc.h>

ParFor* createPFor(uint32 nthr, uint32 niter, int32 wait, void(*body)(ParFor*, uint32)) {
    AT(nthr > 0);
    ParFor* r = malloc(sizeof(ParFor) + nthr*sizeof(ParForThr));
    
    r->body = body;
    r->niter = niter;
    r->nthr = nthr;
    r->thread_ctr = 0;
    r->wait = wait;
    
    uint64 begin, end;
    for(int i=0; i<nthr; ++i) {
        begin = (uint64)niter*i / nthr;
        end = ((uint64)niter*(i+1) / nthr);
        AZ(begin & INT64_HIGH_MASK && "Overflow");
        AZ(end & INT64_HIGH_MASK && "Overflow");
        r->thr[i].bounds = begin | (end << 32);
    }
}

void ParForDoLoop(ParFor* pf) {
    uint32 this_n = atomic_xadd_lag32(&pf->thread_ctr, 1);
    AT(this_n < pf->nthr && "Too many threads");
    ParForThr* this_thr = &pf->thr[this_n];
    
    //isomorphic to serial case
    if(pf->nthr == 1) {
        for(uint32 i=0; i<pf->niter; ++i)
            pf->body(pf, i)
        return;
    }
    
    void (*body)(ParFor*, uint32) = pf->body;
    uint64 bounds,*pbounds;
    pbounds = &pf->bounds;
    for(;;) {
        for(bounds = *pbounds, begin = (uint32)bounds, end = (uint32)(bounds>>32);
            begin < end;
            bounds = atomic_xadd_lag64(pbounds, 1), begin = (uint32)bounds)
        {
            body(pf, begin);
        }
}